/**
* 版权声明：厦门睿商网络科技有限公司 版权所有 违者必究
* 日    期：2020-02-17
*/
package com.rzico.account.service;

import com.alibaba.fastjson.JSON;
import com.rzico.account.entity.*;
import com.rzico.account.mapper.RefundsMapper;
import com.rzico.account.model.Attach;
import com.rzico.base.BaseMapper;
import com.rzico.base.CommResult;
import com.rzico.base.impl.BaseServiceImpl;
import com.rzico.basics.entity.Enterprise;
import com.rzico.basics.entity.Member;
import com.rzico.basics.entity.Vip;
import com.rzico.basics.mapper.MemberMapper;
import com.rzico.basics.mapper.VipMapper;
import com.rzico.basics.service.EnterpriseService;
import com.rzico.basics.service.MemberService;
import com.rzico.core.entity.SysMch;
import com.rzico.core.entity.SysPlugin;
import com.rzico.core.entity.SysUser;
import com.rzico.core.plugin.PaymentPlugin;
import com.rzico.core.service.SysMchService;
import com.rzico.core.service.SysPluginService;
import com.rzico.core.service.SysSequenceService;
import com.rzico.core.service.SysUserService;
import com.rzico.exception.CustomException;
import com.rzico.util.DateUtils;
import io.swagger.models.auth.In;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.rzico.account.mapper.PaymentMapper;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * <pre>
 * 收款单业务类
 * </pre>
 *
 * @author Rzico Boot
 * @version 1.0
 */
@Service
public class PaymentService extends BaseServiceImpl<Payment, String> {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private PaymentMapper paymentMapper;

    @Autowired
    private RefundsMapper refundsMapper;

    @Autowired @Lazy
    private MemberService memberService;

    @Autowired
    private VipMapper vipMapper;

    @Autowired @Lazy
    private RefundsService refundsService;

    @Autowired @Lazy
    private CardBillService cardBillService;

    @Autowired @Lazy
    private PointService pointService;

    @Autowired @Lazy
    private MatchPointService matchPointService;

    @Autowired @Lazy
    private DepositService depositService;


    @Autowired
    private SysMchService sysMchService;

    @Autowired @Lazy
    private SysPluginService sysPluginService;


    @Autowired
    private EnterpriseService enterpriseService;

    @Autowired @Lazy
    private SysUserService sysUserService;

    @Autowired
    private SysSequenceService sysSequenceService;

    @Autowired @Lazy
    private ReceivableService receivableService;

    @Override
    public BaseMapper<Payment, String> getMapper() {
        return paymentMapper;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByIds(Object [] ids) {
        int rw = 0;
        for (Object id:ids) {
            Payment payment = selectByPrimaryKey(id);
            payment.setDeleted(true);
            rw = rw + super.updateByPrimaryKeySelective(payment);
        }
        return rw;
    }

    public Payment findBySn(String sn){
        Map<String,Object> params = new HashMap<>();
        params.put("sn",sn);
        List<Payment> payments = super.selectList(params);
        if (payments.size()>0) {
            return payments.get(0);
        } else {
            return null;
        }
    }

    public List<Payment> getItemList(Long orderType, Long orderId) {
        Map<String,Object> params = new HashMap<>();
        params.put("orderId",orderId);
        params.put("orderType",orderType);
        return selectList(params);
    }

    public void handle(String sn,String tranSn) throws Exception {
        Payment payment = findBySn(sn);
        if (!payment.getStatus().equals(Payment.STATUS_SUCCESS)) {

            if (payment.getPaymentPluginId().equals("weixinPayPlugin") ||
                payment.getPaymentPluginId().equals("weixinMiniPayPlugin")
               ) {
                payment.setFee(payment.getAmount().multiply(new BigDecimal(0.006)).setScale(2,BigDecimal.ROUND_HALF_DOWN));
            }

            payment.setPaymentDate(new Date());
            payment.setStatus(Payment.STATUS_SUCCESS);
            payment.setTranSn(tranSn);
            paymentMapper.updateByPrimaryKeySelective(payment);

            //通过MQ异步通知支付成功
            if (payment.getAttach()!=null) {
                Attach attachQueue = payment.decodeAttach();
                attachQueue.setCode("0000");

                if (payment.getType().equals(Payment.TYPE_VIP)) {
                    Member member = memberService.selectByPrimaryKey(payment.getMemberId());
                    Date d = new Date();
                    if (member.getVipExpired()!=null && member.getVipExpired().after(d)) {
                        d = member.getVipExpired();
                    }
                    Integer vipType = Integer.parseInt(attachQueue.getId());
                    if (vipType==0) {
                        d = org.apache.commons.lang3.time.DateUtils.addMonths(d, 1);
                    } else
                    if (vipType==1) {
                        d = org.apache.commons.lang3.time.DateUtils.addYears(d, 1);
                    } else {
                        d = org.apache.commons.lang3.time.DateUtils.addYears(d, 99);
                    }
                    d = org.apache.commons.lang3.time.DateUtils.truncate(d,Calendar.DATE);
                    member.setVipExpired(d);
                    memberService.updateByPrimaryKeySelective(member);
                } else {
                    Message message = MessageBuilder
                            .withBody(JSON.toJSONString(attachQueue).getBytes())
                            .setContentType(MessageProperties.CONTENT_TYPE_JSON)
                            .build();
                    CorrelationData correlationData = new CorrelationData();
                    correlationData.setId(UUID.randomUUID().toString());
                    logger.info("发送 MQ");
                    rabbitTemplate.convertAndSend("directExchange", attachQueue.getQueue() + ".key", message, correlationData);
                }
            }

        }
    }

    public void query(Long id) throws Exception {

        Payment payment = selectByPrimaryKey(id);

        Boolean expired = payment.getCreateDate().before(org.apache.commons.lang3.time.DateUtils.addMinutes(new Date(),-30));

        PaymentPlugin paymentPlugin = sysPluginService.getPaymentPlugin(payment.getPaymentPluginId());

        Enterprise enterprise = enterpriseService.findById(payment.getEnterpriseId());

        SysMch sysMch = sysMchService.selectByPrimaryKey(enterprise.getMchId());
        if (sysMch.getMchType().equals(2)) {
            //特约商户,服务商代收
            enterprise = enterpriseService.findByMch(sysMch.getIsvId());
        }

        if (payment.getPaymentPluginId()==null) {
            if (expired) {
                //过期关闭订单
                payment.setTranSn("过期关闭");
                payment.setStatus(Payment.STATUS_CANCELED);
                super.updateByPrimaryKeySelective(payment);
            }
            return;
        }

        SysPlugin sysPlugin = sysPluginService.findByPlugin(enterprise.getMchId(),payment.getPaymentPluginId());
        if (sysPlugin==null) {
           if (expired) {
               //过期关闭订单
               payment.setTranSn("过期关闭");
               payment.setStatus(Payment.STATUS_CANCELED);
               super.updateByPrimaryKeySelective(payment);
           }
           return;
        }

        Map<String,Object> resultMap = paymentPlugin.tradeQuery(sysPlugin,payment.getSn(),payment.getTranSn(),null);

        if ("0000".equals(resultMap.get("return_code").toString())) {
            try {
                handle(payment.getSn(),resultMap.get("tranSn").toString());
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        } else {
            if (!"0001".equals(resultMap.get("return_code").toString()) && !"9999".equals(resultMap.get("return_code").toString())) {
                payment.setStatus(Payment.STATUS_FAILURE);
                super.updateByPrimaryKeySelective(payment);
            }
        }

    }

    @Transactional(rollbackFor = Exception.class)
    public void failure(String sn) throws Exception {
        Payment payment = findBySn(sn);
        if (payment.getStatus().equals(Payment.STATUS_WAITING)) {
            payment.setStatus(Payment.STATUS_FAILURE);
            paymentMapper.updateByPrimaryKeySelective(payment);

            //通过MQ异步通知支付成功
            if (payment.getAttach()!=null) {
                Attach attachQueue = payment.decodeAttach();
                attachQueue.setCode("0003");
                Message message = MessageBuilder
                        .withBody(JSON.toJSONString(attachQueue).getBytes())
                        .setContentType(MessageProperties.CONTENT_TYPE_JSON)
                        .build();
                CorrelationData correlationData = new CorrelationData();
                correlationData.setId(UUID.randomUUID().toString());
                rabbitTemplate.convertAndSend("directExchange",attachQueue.getQueue()+".key", message, correlationData);
            }
        }
    }

    //付款（只用于系统内支付方式）
    @Transactional(rollbackFor = Exception.class)
    public void execPayment(Payment payment) {

        if ("cardPayPlugin".equals(payment.getPaymentPluginId())) {
            cardBillService.payment(payment);
        } else
        if ("pointPayPlugin".equals(payment.getPaymentPluginId())) {
            pointService.payment(payment);
        } else
        if ("matchPtPayPlugin".equals(payment.getPaymentPluginId())) {
            matchPointService.payment(payment);
        } else
        if ("balancePayPlugin".equals(payment.getPaymentPluginId())) {
            depositService.payment(payment);
        }  else
        if ("cashPayPlugin".equals(payment.getPaymentPluginId())) {

            payment.setStatus(Payment.STATUS_WAITING);
            payment.setPaymentDate(new Date());
            updateByPrimaryKeySelective(payment);

            Receivable receivable = new Receivable();
            receivable.setAmount(payment.getAmount());
            receivable.setAmountPaid(BigDecimal.ZERO);
            receivable.setType(0);
            receivable.setQuantity(0);
            receivable.setQuantityPaid(0);
            //订单未付款
            receivable.setStatus(0);
            receivable.setPaymentPluginId("debtPayPlugin");
            receivable.setPaymentPluginName("欠款");
            receivable.setDeleted(false);
            receivable.setMemberId(payment.getMemberId());
            receivable.setEnterpriseId(payment.getEnterpriseId());
            receivable.setOrderId(payment.getOrderId());
            receivableService.insertUseGeneratedKeys(receivable);

        }  else
        if ("paperPayPlugin".equals(payment.getPaymentPluginId())) {
            payment.setStatus(Payment.STATUS_WAITING);
            payment.setPaymentDate(new Date());
            if (payment.getQuantity()==null) {
                payment.setQuantity(BigDecimal.ZERO);
            }
            updateByPrimaryKeySelective(payment);

            Receivable receivable = new Receivable();
            receivable.setAmount(payment.getAmount());
            receivable.setAmountPaid(payment.getAmount());
            receivable.setType(0);
            receivable.setQuantity(payment.getQuantity().intValue());
            receivable.setQuantityPaid(0);
            //订单未付款
            receivable.setStatus(0);
            receivable.setPaymentPluginId("debtPayPlugin");
            receivable.setPaymentPluginName("欠款");
            receivable.setDeleted(false);
            receivable.setMemberId(payment.getMemberId());
            receivable.setEnterpriseId(payment.getEnterpriseId());
            receivable.setOrderId(payment.getOrderId());
            receivableService.insertUseGeneratedKeys(receivable);

        }  else
        if ("bankPayPlugin".equals(payment.getPaymentPluginId())) {
            payment.setStatus(Payment.STATUS_SUCCESS);
            payment.setPaymentDate(new Date());
            updateByPrimaryKeySelective(payment);
//        }  else
//        if ("debtPayPlugin".equals(payment.getPaymentPluginId())) {
//
//            payment.setStatus(Payment.STATUS_SUCCESS);
//            payment.setPaymentDate(new Date());
//            updateByPrimaryKeySelective(payment);
//
//            //同时生成应付款
//            Receivable receivable = new Receivable();
//            receivable.setAmount(payment.getAmount());
//            receivable.setAmountPaid(BigDecimal.ZERO);
//            receivable.setType(0);
//            //订单未付款
//            receivable.setStatus(0);
//            receivable.setPaymentPluginId("debtPayPlugin");
//            receivable.setPaymentPluginName("月结");
//            receivable.setDeleted(false);
//            receivable.setMemberId(payment.getMemberId());
//            receivable.setEnterpriseId(payment.getEnterpriseId());
//            receivable.setOrderId(payment.getOrderId());
//            receivableService.insertUseGeneratedKeys(receivable);

        }

    }

    /**
     * 原路退款
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public List<Refunds> refund(Long id,String sn){
        Payment payment = selectByPrimaryKey(id);

        List<Refunds> refundList = new ArrayList<>();
        BigDecimal amount = payment.getAmount().subtract(payment.getRefund());
        if (amount.compareTo(BigDecimal.ZERO)<=0) {
            throw new CustomException("不能重复退款");
        }

        Refunds refunds = new Refunds();
        BeanUtils.copyProperties(payment, refunds);

        refunds.setSn(sn);

        refunds.setMemo("<退款>"+refunds.getMemo());
        refunds.setStatus(0);
        refunds.setPaymentId(payment.getId());
        refunds.setDeleted(false);

        refunds.setAmount(amount);

        refundsMapper.insertUseGeneratedKeys(refunds);

        payment.setRefund(payment.getRefund().add(amount));
        paymentMapper.updateByPrimaryKeySelective(payment);

        refundsService.execRefunds(refunds);

        if (!refunds.getStatus().equals(Refunds.STATUS_REFUNDED) && !refunds.getStatus().equals(Refunds.STATUS_SUCCESS)) {
            //TODO 返回前端去异步通知退款
            refundList.add(refunds);
        }

        return refundList;

    }


    //收款统计
    public List<Map<String,Object>>  summary(Map<String, Object> params) {
        return paymentMapper.summary(params);
    }


    public int insert(List<Payment> list) throws Exception{
        int rw = 0;
        SysUser sysUser = sysUserService.getCurrent();
        Enterprise enterprise = enterpriseService.getCurrent();
        for (Payment payment : list){
            payment.setSn(sysSequenceService.generate("order"));
            if (null == payment.getFee()){
                payment.setFee(BigDecimal.ZERO);
            }
            //1、保存付款单
            payment.setCreateDate(new Date());
            payment.setPaymentDate(new Date());
            payment.setDeleted(false);
            payment.setType(2);
            //线下收款
            payment.setMethod(1);
            //等待支付
            payment.setStatus(0);
            payment.setEnterpriseId(enterprise.getId());
            payment.setEmployeeId(sysUser.getId());
            payment.setOrderType(0);
            //2、回写应收款单的已收款金额、状态
            Receivable receivable = receivableService.selectByPrimaryKey(payment.getReceivableId());
            payment.setOrderType(receivable.getOrderType());
            payment.setOrderId(receivable.getOrderId());
            payment.setOrderSn(receivable.getOrderSn());

            Attach attach = new Attach();
            attach.setId(String.valueOf(payment.getOrderId()));
            attach.setCode("9999");
            attach.setQueue("queue.order.payment");
            attach.setDescription(payment.getMemo());
            payment.setAttach(JSON.toJSONString(attach));

            int affectCount = paymentMapper.insertUseGeneratedKeys(payment);
            if (affectCount > 0){
                confirm(payment.getId());
                rw = rw+1;
            }

        }
        return rw;
    }

    /**
     *  确认收款
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int confirm(Long id) {
        Payment payment = selectByPrimaryKey(id);
        if (null == payment){
            throw new CustomException("收款单不存在");
        }
        if (payment.getStatus() != 0){
            throw new CustomException("收款单无需确认");
        }

        payment.setStatus(1);
        int affectCount = paymentMapper.updateByPrimaryKeySelective(payment);
        if (payment.getReceivableId()!=null) {
            Receivable receivable = receivableService.selectByPrimaryKey(payment.getReceivableId());
            receivable.setAmountPaid(receivable.getAmountPaid().add(payment.getAmount()));
            receivable.setQuantityPaid(receivable.getQuantityPaid()+payment.getQuantity().intValue());
            if (receivable.getAmountPaid().compareTo(receivable.getAmount())>0) {
                throw new CustomException("不能重复收款");
            } else
            if (receivable.getAmountPaid().compareTo(receivable.getAmount())==0) {
                receivable.setStatus(2);
                receivableService.updateByPrimaryKeySelective(receivable);
            }
        }
        return affectCount;

    }


    /**
     *  修单收款单
     * @param payments
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int update(List<Payment> payments) {
        int affectCount = 0;
        for (Payment payment:payments) {

            if (payment.getStatus() != 0) {
                throw new CustomException("已确认不可修改");
            }

            Receivable receivable = null;
            if (payment.getReceivableId()!=null) {
                receivable = receivableService.selectByPrimaryKey(payment.getReceivableId());
            }
            payment.setPaymentDate(new Date());

            if (payment.getId()==null) {
                Attach attach = new Attach();
                attach.setId(String.valueOf(payment.getOrderId()));
                attach.setCode("9999");
                attach.setQueue("queue.order.payment");
                attach.setDescription(payment.getMemo());
                payment.setAttach(JSON.toJSONString(attach));

                payment.setCreateDate(new Date());
                paymentMapper.insertUseGeneratedKeys(payment);

            } else {
                paymentMapper.updateByPrimaryKeySelective(payment);

            }

        }
        return 1;

    }


    /**
     *  取消收款
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public int cancel(Long id) {
        Payment payment = selectByPrimaryKey(id);
        if (null == payment){
            throw new CustomException("付款单不存在");
        }
        if (null == payment.getReceivableId()){
            throw new CustomException("收款不可取消");
        }
        if (payment.getStatus() != 0){
            throw new CustomException("已确认不可取消");
        }
        payment.setStatus(2);

        int affectCount = paymentMapper.updateByPrimaryKeySelective(payment);
        if (affectCount > 0) {

            Receivable receivable = receivableService.selectByPrimaryKey(payment.getReceivableId());

            //已收金额 = 应收款已收金额 - 本次取消金额
            BigDecimal hasReceviedAmount = receivable.getAmountPaid().subtract(payment.getAmount());
            int result = hasReceviedAmount.compareTo(receivable.getAmount());
            if (result >= 0){
                receivable.setStatus(2);
            }else{
                receivable.setStatus(1);
            }
            receivable.setAmountPaid(hasReceviedAmount);
            receivableService.updateByPrimaryKeySelective(receivable);

        }
        return affectCount;
    }

}
